package com.demo.service.impl;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.demo.constant.CacheConstants;
import com.demo.domain.SysDept;
import com.demo.enums.DeletedEnum;
import com.demo.enums.UserStatusEnum;
import com.demo.excetion.ServiceException;
import com.demo.mapper.SysDeptMapper;
import com.demo.service.SysDeptService;
import com.demo.utils.BeanUtils;
import com.demo.utils.PageUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author gxd
 * @since 2025/5/27
 */
@Slf4j
@Service
public class SysDeptServiceImpl extends ServiceImpl<SysDeptMapper, SysDept> implements SysDeptService {

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 方式一：使用 @PostConstruct，项目启动加载部门数据到Redis缓存
     */
    // @PostConstruct
    // public void init() {
    //     loadingDeptCache();
    // }

    /**
     * 加载部门数据到缓存
     */
    @Override
    public void loadingDeptCache() {
        // 平铺存储：key：sys:dept:one:{id}, value：部门对象
        long l1 = System.currentTimeMillis();
        List<SysDept> deptList = this.pageList(new SysDept());
        Map<String, SysDept> deptMap = deptList.stream()
                .collect(Collectors.toMap(dept -> CacheConstants.DEPT_ONE_KEY + dept.getDeptId(), Function.identity()));
        long l2 = System.currentTimeMillis();
        redisTemplate.opsForValue().multiSet(deptMap);
        log.info("--------部门写入redis耗时----------{}", (l2 - l1)  );
        // 树形结构存储：key：sys:dept:tree, value: 树结构json
        List<SysDept> treeList = deptList.stream()
                .filter(dept -> ObjUtil.equals("0", dept.getParentId()))
                .map(dept -> {
                    dept.setChildList(getChildList(dept, deptList));
                    return dept;
                }).collect(Collectors.toList());
        redisTemplate.opsForValue().set(CacheConstants.DEPT_TREE_KEY, treeList);
    }

    /**
     * 获取子级部门集合
     */
    private List<SysDept> getChildList(SysDept sysDept, List<SysDept> deptList) {
        return deptList.stream()
                .filter(dept -> ObjUtil.equals(sysDept.getDeptId(), dept.getParentId()))
                .map(dept -> {
                    dept.setChildList(getChildList(dept, deptList));
                    return dept;
                }).collect(Collectors.toList());
    }

    /**
     * 分页列表-部门列表
     * @param dept 部门查询条件
     * @return 部门集合
     */
    @Override
    public List<SysDept> pageList(SysDept dept) {
        long l1 = System.currentTimeMillis();
        LambdaQueryWrapper<SysDept> lqw = Wrappers.lambdaQuery(SysDept.class)
                .eq(SysDept::getDelFlag, String.valueOf(DeletedEnum.NO.getCode()))
                .eq(SysDept::getStatus, UserStatusEnum.OK.getCode());

        List<SysDept> allList = new ArrayList<>();

        long count = this.count(lqw);
        int rowCount = 3000;
        List<Integer> pageCountList = PageUtils.manualPage(count, rowCount);

        pageCountList.parallelStream().forEach(pageCount -> {
            Page<SysDept> page = this.page(new Page<>(pageCount, rowCount), lqw);
            List<SysDept> deptList = ObjUtil.defaultIfNull(page.getRecords(), new ArrayList<>());
            allList.addAll(deptList);
        });

        // 方式二：自定义线程池，并发执行
       /* ExecutorService fixedThreadPool = Executors.newFixedThreadPool(20);
        List<CompletableFuture<Void>> completableFutures = new ArrayList<>();
        pageCountList.forEach(pageCount -> {
            CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
                Page<SysDept> page = this.page(new Page<>(pageCount, rowCount), lqw);
                List<SysDept> deptList = ObjUtil.defaultIfNull(page.getRecords(), new ArrayList<>());
                allList.addAll(deptList);
            }, fixedThreadPool);
            completableFutures.add(completableFuture);
        });
        CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0])).join();*/

        long l2 = System.currentTimeMillis();
        log.info("--------部门耗时time----------{}", (l2 - l1)  );
        return allList;
    }

    /**
     * 不分页列表-部门列表
     * @param dept 部门查询条件
     * @return 部门集合
     */
    @Override
    public List<SysDept> selectDeptList(SysDept dept) {
        return this.list(
                Wrappers.lambdaQuery(SysDept.class)
                        .eq(SysDept::getDelFlag, String.valueOf(DeletedEnum.NO.getCode()))
                        .like(StrUtil.isNotBlank(dept.getDeptName()), SysDept::getDeptName, dept.getDeptName())
                        .eq(StrUtil.isNotBlank(dept.getStatus()), SysDept::getStatus, dept.getStatus())
                        .orderByDesc(SysDept::getCreateTime)
        );
    }

    /***
     * 详情信息-根据Id获取部门详情信息
     * @param id 部门Id
     * @return 部门
     */
    @Override
    public SysDept getDeptById(String id) {
        SysDept sysDept = (SysDept) redisTemplate.opsForValue().get(CacheConstants.DEPT_ONE_KEY + id);
        if (ObjUtil.isNull(sysDept)) {
            sysDept = this.getById(id);
            redisTemplate.opsForValue().set(CacheConstants.DEPT_ONE_KEY + id, sysDept);
        }
        return sysDept;
    }

    /**
     * 添加或修改-部门信息
     * @param dept 部门实体
     * @return 部门
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public SysDept addOrEdit(SysDept dept) {
        Assert.notNull(dept, "部门信息不能为空");
        // 使用延时删除，实现redis和mysql数据同步
        // 延时删除：先删除缓存，再更新数据库，再异步延时删除 更新缓存
        // 异步延迟1-2秒，如果查询接口300毫秒，延时400-500毫秒，如果查询接口500毫秒，延时600毫秒
        if (StrUtil.isNotBlank(dept.getDeptId())) {
            String deptId = dept.getDeptId();
            boolean lockAcquired = false;
            String lockKey = CacheConstants.DEPT_LOCK_KEY + deptId;
            try {
                lockAcquired = redisTemplate.opsForValue().setIfAbsent(lockKey, "给该部门上锁", 5, TimeUnit.SECONDS);

                if (Boolean.FALSE.equals(lockAcquired)) {
                    throw new ServiceException("当前部门正在被操作，请重试");
                }

                // 删除单条缓存，树不处理
                cleanDeptCache(deptId);

                // 更新数据库
                SysDept deptInfo = Assert.notNull(this.getById(dept.getDeptId()), "要修改的部门信息不存在");
                BeanUtils.copyNotEmptyProperties(dept, deptInfo);
                this.updateById(deptInfo);

                // 异步延时删除再更新
                CompletableFuture.runAsync(() -> {
                    try {
                        Thread.sleep(400);
                        // 删除单条缓存，树不处理
                        cleanDeptCache(deptId);

                        // 更新单条缓存
                        redisTemplate.opsForValue().set(CacheConstants.DEPT_ONE_KEY + deptId, deptInfo);
                        // 更新树缓存
                        updateDeptTreeCache(deptInfo);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                });

                return deptInfo;
            } finally {
                redisTemplate.delete(lockKey);
            }
        }
        dept.setDelFlag(String.valueOf(DeletedEnum.NO.getCode()));
        this.save(dept);

        // 缓存单条
        redisTemplate.opsForValue().set(CacheConstants.DEPT_ONE_KEY + dept.getDeptId(), dept);
        // 异步延迟更新树缓存
        CompletableFuture.runAsync(() -> {
            try {
                Thread.sleep(400);
                // 更新树缓存
                updateDeptTreeCache(dept);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        return dept;
    }

    /**
     * 更新部门树缓存
     */
    private void updateDeptTreeCache(SysDept deptInfo) {
        String treeKey = CacheConstants.DEPT_ONE_KEY;
        if (redisTemplate.hasKey(treeKey)) {
            List<SysDept> deptTreeList = (List<SysDept>) redisTemplate.opsForValue().get(treeKey);
            List<SysDept> deptList = flattenTree(deptTreeList);

            // 修改
            Boolean updated = deptList.stream()
                    .filter(sysDept -> ObjUtil.equals(deptInfo.getDeptId(), sysDept.getDeptId()))
                    .findFirst()
                    .map(sysDept -> {
                        deptList.set(deptTreeList.indexOf(sysDept), deptInfo);
                        return true;
                    })
                    .orElse(false);

            // 添加
            if (!updated) {
                deptList.add(deptInfo);
            }

            // 重置子级
            deptList.replaceAll(sysDept -> {
                sysDept.setChildList(null);
                return sysDept;
            });

            redisTemplate.opsForValue().set(treeKey, buildDeptTree(deptList));
        } else {
            redisTemplate.opsForValue().set(treeKey, buildDeptTree());
        }
    }

    /**
     * 将树结构的list转为平铺的list
     */
    private List<SysDept> flattenTree(List<SysDept> deptTreeList) {
        return deptTreeList.stream()
                .flatMap(sysDept -> Stream.concat(Stream.of(sysDept), flattenTree(sysDept.getChildList()).stream()))
                .collect(Collectors.toList());
    }

    /***
     * 删除-部门信息（支持批量）
     * @param ids 部门Id（多个需逗号分隔）
     * @return 结果
     */
    @Override
    public boolean batchRemoveById(String ids) {
        Assert.notEmpty(ids, "缺少必要参数：请提交要删除的记录 ID");
        Arrays.stream(ids.split(StrPool.COMMA))
                .forEach(id -> {
                    boolean lockAcquired = false;
                    String lockKey = CacheConstants.DEPT_LOCK_KEY + id;
                    try {
                        lockAcquired = redisTemplate.opsForValue().setIfAbsent(lockKey, "给该部门上锁", 5 , TimeUnit.SECONDS);

                        if (Boolean.FALSE.equals(lockAcquired)) {
                            throw new ServiceException("当前部门正在被操作，请重试");
                        }

                        cleanDeptCache(id);

                        this.update(Wrappers.lambdaUpdate(SysDept.class)
                                .set(SysDept::getDelFlag, String.valueOf(DeletedEnum.YES.getCode())).eq(SysDept::getDeptId, id));

                        CompletableFuture.runAsync(() -> {
                           try {
                               Thread.sleep(400);
                               cleanDeptCache(id);

                               SysDept sysDept = this.getById(id);
                               // 缓存单条
                               redisTemplate.opsForValue().set(CacheConstants.DEPT_ONE_KEY + id, sysDept);
                               // 更新树缓存
                               updateDeptTreeCache(sysDept);
                           } catch (InterruptedException e) {
                               throw new RuntimeException(e);
                           }
                       });
                    } finally {
                        redisTemplate.delete(lockKey);
                    }
                });
        return true;
    }

    /**
     * 树查询-部门树
     * @return 部门树
     */
    @Override
    public List<SysDept> getDeptTree() {
        List<SysDept> deptList = (List<SysDept>) redisTemplate.opsForValue().get(CacheConstants.DEPT_TREE_KEY);
        if (ObjUtil.isNull(deptList)) {
            redisTemplate.opsForValue().set(CacheConstants.DEPT_TREE_KEY, buildDeptTree());
        }
        return deptList;
    }

    /**
     * 从数据库查询，构建部门树
     * @return 部门树
     */
    public List<SysDept> buildDeptTree() {
        return buildDeptTree(pageList(new SysDept()));
    }

    /**
     * 查询部门树
     */
    public List<SysDept> buildDeptTree(List<SysDept> deptList) {
        // 分组
        Map<String, List<SysDept>> deptGroupMap = deptList.stream()
                .collect(Collectors.groupingBy(SysDept::getParentId));
        // 组装子级部门集合
        deptList.forEach(dept -> dept.setChildList(deptGroupMap.getOrDefault(dept.getDeptId(), new ArrayList<>())));
        // 输出部门树
        return deptList.stream()
                .filter(dept -> ObjUtil.equals("0", dept.getParentId()))
                .collect(Collectors.toList());
    }

    /**
     * 清除单条部门缓存
     */
    public void cleanDeptCache(String id) {
        if (StrUtil.isNotBlank(id)) {
            redisTemplate.delete(CacheConstants.DEPT_ONE_KEY + id);
        };
    }
}
